/**
* Copyright (c) By zengqh.
*
* This program is just for fun or demo, in the hope that it  
* will be useful, you can redistribute it and/or modify freely.
*
* Time: 2013/05/25
* File: enn_thread.cpp
* Blog: http://www.cnblogs.com/zengqh/
**/

#include <enn_thread.h>

namespace enn
{
//////////////////////////////////////////////////////////////////////////
#if ENN_PLATFORM == ENN_PLATFORM_WIN32
DWORD WINAPI ThreadFunctionStatic(void* data)
{
	Thread* thread = static_cast<Thread*>(data);
	thread->ThreadFunction();
	return 0;
}
#else
void* ThreadFunctionStatic(void* data)
{
	Thread* thread = static_cast<Thread*>(data);
	thread->ThreadFunction();
	pthread_exit((void*)0);
	return 0;
}
#endif

Thread::Thread() :
handle_(0),
	shouldRun_(false)
{
}

Thread::~Thread()
{
	Stop();
}

bool Thread::Start()
{
	// Check if already running
	if (handle_)
		return false;

	shouldRun_ = true;
#if ENN_PLATFORM == ENN_PLATFORM_WIN32
	handle_ = CreateThread(0, 0, ThreadFunctionStatic, this, 0, 0);
#else
	handle_ = new pthread_t;
	pthread_attr_t type;
	pthread_attr_init(&type);
	pthread_attr_setdetachstate(&type, PTHREAD_CREATE_JOINABLE);
	pthread_create((pthread_t*)handle_, &type, ThreadFunctionStatic, this);
#endif
	return handle_ != 0;
}

void Thread::Stop()
{
	// Check if already stopped
	if (!handle_)
		return;

	shouldRun_ = false;
#if ENN_PLATFORM == ENN_PLATFORM_WIN32
	WaitForSingleObject((HANDLE)handle_, INFINITE);
	CloseHandle((HANDLE)handle_);
#else
	pthread_t* thread = (pthread_t*)handle_;
	if (thread)
		pthread_join(*thread, 0);
	delete thread;
#endif
	handle_ = 0;
}

void Thread::SetPriority(int priority)
{
#if ENN_PLATFORM == ENN_PLATFORM_WIN32
	if (handle_)
		SetThreadPriority((HANDLE)handle_, priority);
#endif
#if defined(__linux__) && !defined(ANDROID)
	pthread_t* thread = (pthread_t*)handle_;
	if (thread)
		pthread_setschedprio(*thread, priority);
#endif
}

//////////////////////////////////////////////////////////////////////////
#if ENN_PLATFORM == ENN_PLATFORM_WIN32
Mutex::Mutex() :
handle_(new CRITICAL_SECTION)
{
	InitializeCriticalSection((CRITICAL_SECTION*)handle_);
}

Mutex::~Mutex()
{
	CRITICAL_SECTION* cs = (CRITICAL_SECTION*)handle_;
	DeleteCriticalSection(cs);
	delete cs;
	handle_ = 0;
}

void Mutex::Acquire()
{
	EnterCriticalSection((CRITICAL_SECTION*)handle_);
}

void Mutex::Release()
{
	LeaveCriticalSection((CRITICAL_SECTION*)handle_);
}
#else
Mutex::Mutex() :
handle_(new pthread_mutex_t)
{
	pthread_mutex_t* mutex = (pthread_mutex_t*)handle_;
	pthread_mutexattr_t attr;
	pthread_mutexattr_init(&attr);
	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
	pthread_mutex_init(mutex, &attr);
}

Mutex::~Mutex()
{
	pthread_mutex_t* mutex = (pthread_mutex_t*)handle_;
	pthread_mutex_destroy(mutex);
	delete mutex;
	handle_ = 0;
}

void Mutex::Acquire()
{
	pthread_mutex_lock((pthread_mutex_t*)handle_);
}

void Mutex::Release()
{
	pthread_mutex_unlock((pthread_mutex_t*)handle_);
}
#endif

//////////////////////////////////////////////////////////////////////////
MutexLock::MutexLock(Mutex& mutex) :
mutex_(mutex)
{
	mutex_.Acquire();
}

MutexLock::~MutexLock()
{
	mutex_.Release();
}

}

